<?php 

/** 
 * FileManager 
 * 
 * This implements the class to manage files templates 
 * 
 * You can load the template from the several types of the files 
 *  and you can pass the file name and the fields to the file processor 
 * 
 * Your file must contain the {{key}} from the fields that do you want 
 * 
 * @author Sysprog <sysprog@martis-ua.com> 
 * @link http://www.seo-design.kiev.ua/ 
 * @copyright Copyright &copy; SEO Design 2014 
 * @license http://www.seo-design.kiev.ua/license/  
 * @version 1.0.0.1 $ 
 */ 

  require_once ( "fpdf17/tfpdf.php" ); 

	class FileManager { 

		const OBJ_PDF = 0; 
		const OBJ_JPG = 1; 
		const OBJ_DOC = 2; 
		const OBJ_TXT = 3; 

    private $filePath = ""; 

    private $fileType = -1; 

    private $dataProvider = null; 

    private $tables = array(); 

    /** 
     * Stores the positions of the objects in $this->content 
     */ 
    private $shifts = array(); 

    /** 
     * The sets of the objects on the order of shifts 
     */ 
    private $outputDOM = array();

    private $pdf = null; 

    public function getDocsFile( $dataProvider , $filePath = "") { 

        $this->dataProvider = $dataProvider; 

        if( !empty( $filePath ) ) { 

            $this->filePath = $filePath; 

        } 

        switch(CFileHelper::getExtension( $this->filePath )) { 

            case  "pdf" : 
            case  "PDF" : 

              $this->fileType = FileManager::OBJ_PDF; 

            break; 

            case "jpg" : 
            case "JPG" : 

              $this->fileType = FileManager::OBJ_JPG; 

            break; 

            case "doc" : 
            case "DOC" : 

            case "docx": 
            case "DOCX": 

              $this->fileType = FileManager::OBJ_DOC; 

            break; 

            case "txt" : 
            case "TXT" : 

              $this->fileType = FileManager::OBJ_TXT; 

            break; 

        } 

        //echo $this->fileType." ".CFileHelper::getExtension( $this->filePath ); 

        $this->parseTextFile(); 

        //$this->replaceContentTags(); 

        $this->flushPDF(); 
    } 

    private function parseTextFile () 
    {

        $this->content = $content = ""; 

        try { 

            $path = $this->filePath; 

            if ( !is_file ( $path ) && is_file( Yii::app()->basePath."/..".$path ) ) { 

                 $path = Yii::app()->basePath."/..".$path ; 

            } 

            $myfile = fopen( $path, "r" ); 

            if ( $myfile != NULL ) { 
                while( !feof( $myfile ) ) { 
                    $content .= fgets( $myfile ) ; 
                } 
            } 

            fclose( $myfile ); 

        } 
        catch ( Exception $ex ) { 
            print_r($ex); 
        } 

        return $this->content = $content ; 
    }

    private function getLines () { 

        $positions = array (); 

        $shift = 0; 
        while( $shift !== false && ( $shift < strlen ( $this->content ) ) ) { 

            $linePos = stripos( $this->content, "<line", $shift ); 

            if( $linePos !== false ) { 

                $linePos2 = stripos( $this->content, ">", $linePos ); 

                preg_match_all ( '/(<line[^>]*?(width=[\'"](?<width>[\w]+)[\'"])\>)/mi', substr( $this->content, $linePos, $linePos2 + 2 ), $line, PREG_SET_ORDER ); 

                $sh = array( 'start' => $linePos, 'end' => $linePos2 ); 
                $this->shifts[$linePos] = $sh; 

                $obj = array ( 

                    "object" => $line , 
                    "shifts" => $this->shifts[$linePos], 
                    "type" => "line", 
                    "tag" => "line" 
 
                ); 

                $this->outputDOM[$linePos] = $obj; 

                $this->insertObject( $this->shifts[$linePos], $this->outputDOM[$linePos] ); 

                $shift = $linePos2 + 1; 
            } 
            else { 
                $shift = false; 
            } 

        }

    } 
    
    /** 
     * Searches for <details_table key="[key-name]"> 
     */ 
    private function getTableDetails( $key = "tbl" ) { 

        $code = $this->getTableMarkup ( $key ); 

        // Search for the table header 
        $tablePos = stripos( $code, "<details_header>" ); 
        $tablePos1 = stripos( $code, "</details_header>" ); 

        $codeHeader = substr( $code, $tablePos, $tablePos1 - $tablePos ); 

        preg_match_all( '/(<(?<tagName>[\w]+)[^>]*?(width=[\'"](?<width>[\w]+)[\'"])*\>)(?<innerHtml>.*?)(<\/\2>)/mi', $codeHeader , $headerItems , PREG_SET_ORDER ); 

        $tablePos = stripos( $code, "<details_row>" ); 
        $tablePos1 = stripos( $code, "</details_row>" ); 

        $codeRow = substr( $code, $tablePos, $tablePos1 - $tablePos );  

        preg_match_all( '/(<(?<tagName>[\w]+)[^>]*?(width=[\'"](?<width>[\w]+)[\'"])*[\s]*(align=[\'"](?<align>[\w]+)[\'"])*\>)(?<innerHtml>.*?)(<\/\2>)/mi', $codeRow , $rowItems , PREG_SET_ORDER ); 

        $tablePos = stripos( $code, "<details_footer>" ); 
        $tablePos1 = stripos( $code, "</details_footer>" ); 

        $codeFooter = substr( $code, $tablePos, $tablePos1 - $tablePos ); 

        preg_match_all( '/(<(?<tagName>[\w]+)[^>]*?(width=[\'"](?<width>[\w]+)[\'"])*\>)(?<innerHtml>.*?)(<\/\2>)/mi', $codeFooter , $footerItems , PREG_SET_ORDER ); 

        $this->tables[$key]["header"] = $headerItems; 
        $this->tables[$key]["row"] = $rowItems; 

        if ( count( $footerItems ) > 0 ) { 
            $this->tables[$key]["footer"] = $footerItems; 
        } 
        else { 
            $this->tables[$key]["footer"] = null; 
        } 

        // Put the table into DOM if any 
        $start = $this->shifts[ $this->tables[$key]['shifts']['start'] ]['start'] ;  
        if( !empty( $this->shifts[$start] ) && $start >= 0 ) { 

            $obj = array ( 

                      "object" => &$this->tables[$key], 
                      "shifts" => $this->shifts[$start], 
                      "type" => 'table', 
                      "tag" => "details_table", 

                   ); 

            $this->outputDOM[$start] = $obj; 

        } 

        // Insert object into DOM and shift the MultiCells 
        $this->insertObject ( $this->shifts[$start] , $this->outputDOM[$start] ); 
    } 

    /** 
     * Insert of the Object into the DOM 
     */ 
    private function insertObject ( $shift, $obj ) { 

        ksort( $this->outputDOM );  
        ksort( $this->shifts ); 

        $step_pre = 0; 
        if ( $obj != null ) { 

            foreach( $this->shifts as $sh => $value ) { 

                if ( $shift["start"] >= $sh ) { 

                       if ( $this->outputDOM[ $step_pre ]['type'] == 'multicell' ) {  // Previous tag is Multi Cell - need to replace the obj tag 

                          $striped = $this->stripTag ( $this->outputDOM[ $step_pre ]['object'] , $this->outputDOM[ $shift["start"] ]['tag'] ); 

                          if( count( $striped ) == 2 ) { 

                             $this->outputDOM [ $step_pre ]['object'] = $striped[0]; 

                             $nextShift = $this->shifts[ $step_pre ]; 
                             $nextShift['start'] = $shift[ "start" ] + 2;  
                             $nextShift['end'] = -1;  // indicates the object is generic 

                             // Create the next multicell after the object 
                             $nextMulticell = array ( 

                                      "object" => $striped[1], 
                                      "shifts" => $nextShift, 
                                      "type" => 'multicell', 
                                      "tag" => "", 

                                   ); 

                             $this->shifts[ $nextShift['start'] ] = $nextShift; 

                             $this->outputDOM [ $shift["start"] + 2 ] = $nextMulticell; // start next 

                          }

                      } 

                } else { 

                    $step_pre = $sh; 

                } 

            } 

        } 

    } 
    
    private function stripTag ( $obj, $tag ) { 

        $result = null; 

        $start = stripos( $obj, '<'.$tag.'' ); 

        $end = stripos( $obj, '</'.$tag.'>' ) ; 

        if( $end === false ) { 

            $end = stripos( $obj, ">", $start ) + 1; 

        } 
        else { 

            $end = $end + strlen( '</'.$tag.'>' ) ; 

        } 

        if ( $start !== false ) { 

            $result = array ( substr( $obj, 0, $start ), substr( $obj, $end, strlen($obj) - $end )  );  

        } 

        return $result ; 
    } 

    private function getMultiCell ( $text = "", $start = 0, $end = 0 ) { 

        $this->shifts[ $start ] = array( "start" => $start, "end" => ( $end > 0 ? $end : strlen( $text ) )  ); 

        $obj = array ( 

                  "object" => $text, 
                  "shifts" => $this->shifts[0], 
                  "type" => "multicell", 
                  "tag" => "", 

               ); 

        $this->outputDOM[$start] = $obj; 

        return $this->content ; 
    } 

    private function getTableMarkup ( $key = "tbl" ) { 

        $tablePos = 0;  //stripos( $this->content, '<details_table key=\"$key\">' ); 
        $tagLen = 0;    //strlen( '<details_table key=\"$key\">' ); 

        $patterns = array( ); 

        $patterns['p1'] = '<details_table key="'.$key.'">'; 
        $patterns['p2'] = '<details_table key=\''.$key.'\'>'; 
        $patterns['p3'] = '<details_table>'; 
        $patterns['p4'] = '<details_table'; 

        foreach( $patterns as $keyp => $pattern ) { 

            $tablePos = stripos( $this->content, $pattern ); 
            $tagLen = strlen( $pattern ); 

            if( $tablePos > 0 ) { 

                break;

            } 

        }

        $endPart = substr( $this->content, $tablePos + $tagLen ); 

        $endPos = stripos( $endPart, "</details_table>" ); 

        $this->shifts[$tablePos] = $this->tables[$key]["shifts"] = array( 'start' => $tablePos, 'end' => $endPos ); 
        $this->tables[$tablePos] = $key;  // allow to save the table order 

        return substr ( $endPart, 0, $endPos ); 
    } 


    /** 
     * Replace tags from the DataAdapter in $this->content 
     */ 
    private function replaceContentTags () { 

        foreach( $this->dataProvider->keys as $key=>$value ) {  

            if( !is_array( $this->dataProvider->data[$key] ) ) { 

                $pattern = array ( '/{{'.$key.'}}/' ); 

                $this->content = preg_replace( $pattern, $this->dataProvider->data[$key], $this->content ); 

            } 
        } 

        foreach( $this->dataProvider->keys as $key=>$value ) {
        
            if( is_array( $this->dataProvider->data[$key] ) ) { 

                $out = array(); 

                $this->getMultiCell ( $this->content ); 

                // Find the details_table 
                $this->getTableDetails ( $key ); 

            }

        } 

        $this->getLines () ; 

    } 
    

    private function flushTable( $table ) { 

        $key = $this->tables[$table]; 
        $obj = $this->tables[$key];
        
        if( !empty($key) ) { 

            $this->pdf->SetFillColor( 255, 255, 255 ); 
            $this->pdf->SetTextColor( 0 ); 
            //$this->pdf->SetDrawColor( 128, 0, 0 ); 
            $this->pdf->SetLineWidth(.3); 

            $totalWidth = 0; 

            foreach( $obj["header"] as $hCell ) { 

                $this->pdf->Cell( $hCell["width"], 7, $hCell["innerHtml"] , 1, 0, 'C', true ); 

                $totalWidth = $totalWidth + $hCell["width"]; 

            } 

            $this->pdf->Ln(); 

            foreach( $this->dataProvider->data[$key] as $row ) { 

                $i = 0 ; 
                foreach ( $row as $cell => $value ) { // values 
                
                    $align = "L"; 
                    switch( $obj['row'][$i]["align"] ) { 

                        case "left": 
                          $align = "L"; 
                        break; 

                        case "center": 
                          $align = "C"; 
                        break; 

                        case "right": 
                          $align = "R"; 
                        break; 

                    } 

                    $pattern = array( '/{{'.$cell.'}}/' ); 

                    $rValue = preg_replace( $pattern, $value, $obj['row'][ $i ]['innerHtml'] ); 

                    $this->pdf->Cell( $obj['row'][$i]["width"], 7, $rValue, 'LR', 0, $align, true ); 

                    //$this->content = preg_replace( $pattern, $this->dataProvider->data[$key], $this->content ); 

                    $i++; 

                } 

                $this->pdf->Ln(); 

            } 

            if ( $obj["footer"] != null ) { 

              foreach( $obj["footer"] as $fCell ) { 

                  $this->pdf->Cell( $fCell["width"], 7, $fCell["innerHtml"] , 1, 0, 'C', true ); 

              } 
            } 
            else { 
                  $this->pdf->Cell( $totalWidth, 0, "", "T" ); 
            } 
 
            $this->pdf->Ln(); 

        }  

    }

    private function flushLine ( $line ) { 

        $this->pdf->Cell( $this->outputDOM[$line]['width'], 0, "", "T" ); 
        $this->pdf->Ln(); 

    } 

    private function flushDOM () { 

        $this->replaceContentTags(  ); 
        
        ksort ( $this->shifts ); 
        ksort ( $this->outputDOM );

        foreach ( $this->outputDOM as $shift => $object ) { 

            switch( $this->outputDOM[$shift]['type'] ) {  

                case "multicell": 

                    $this->pdf->MultiCell( 160, 5, $object['object'] ); 

                break; 

                case "table": 

                    $this->flushTable( $shift ); 

                break; 

                case "line": 

                    $this->flushLine ( $shift ); 

                break; 

            } 
        } 

    }

    private function flushPDF () { 

        $this->pdf = $filePdf = new tFPDF(); 

        $title = "Indast - Деталі замовлення"; 

        $filePdf->SetTitle( $title, true ); 
        $filePdf->SetAuthor( "Indast" ); 

        $filePdf->AddPage(); 

        $filePdf->AddFont( 'DejaVu', '', 'DejaVuSansCondensed.ttf', true); 
        $filePdf->SetFont( 'DejaVu', '', 14 ); 

        //$filePdf->Write( 0, $this->content ); 
        $this->flushDOM (); 

        //$filePdf->MultiCell( 160, 5, $this->content ); 

        $filePdf->Ln(); 

        $filePdf->Output(); 

    }

	}

?>